package com.msb.高级排序;


// 可以去体系学习班学习
// 时间复杂度NlogN
// 归并递归:55min
// 归并循环:>1h
public class Code02_MergeSort {

	// 递归方法实现(各种考虑越界情况)
	// 左边有序, 右边有序, 然后合并
	// 合并, 双指针遍历两个数组, 小就换位置, 然后指针前移
	public static void mergeSort1(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		process(arr, 0, arr.length - 1);
	}

	// 大坑: >>1如果是负数就有问题, 所以/2用移位代替还得慎重
	private static void process(int[] arr, int left, int right) {
		if (left >= right) {
			return;
		}
		// (left + right) / 2可能分子越界
//		int mid = left - ((left - right) >> 1);
		int mid = left + ((right - left) >> 1);
		process(arr, left, mid);
		process(arr, mid + 1, right);
		merge(arr, left, mid, right);
	}

	private static void merge(int[] arr, int left, int mid, int right) {
		int[] temp = new int[right - left + 1];
		int p1 = left;
		int p2 = mid + 1;
		int cur = 0;
		while(p1 <= mid && p2 <= right) {
			temp[cur++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= mid) {
			temp[cur++] = arr[p1++];
		}
		while (p2 <= right) {
			temp[cur++] = arr[p2++];
		}
		for (int i = 0; i < cur; i++) {
			arr[left + i] = temp[i];
		}
	}

	// 循环的解法: 把步长为1的左右合并, 为2的左右合并,为4的...
	// 注意-1边界
	public static void mergeSort2(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		int step  = 1;
		while (step < arr.length) {
			int leftest = arr.length;
			while(leftest > 0) {
				int right = leftest - 1;
				leftest = leftest - 2 * step;
				int left = Math.max(0, leftest);
				int leftMax = Math.max(0, leftest + step - 1);
				merge(arr, left, leftMax, right);
			}
			if (step > arr.length / 2) {
				break;
			}
			step *= 2;
		}
	}

	// 非递归方法实现
//	public static void mergeSort2(int[] arr) {
//		if (arr == null || arr.length < 2) {
//			return;
//		}
//		int N = arr.length;
//		int mergeSize = 1;
//		while (mergeSize < N) {
//			int L = 0;
//			while (L < N) {
//				if (mergeSize >= N - L) {
//					break;
//				}
//				int M = L + mergeSize - 1;
//				int R = M + Math.min(mergeSize, N - M - 1);
//				merge(arr, L, M, R);
//				L = R + 1;
//			}
//			if (mergeSize > N / 2) {
//				break;
//			}
//			mergeSize <<= 1;
//		}
//	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 100000;
		int maxSize = 100;
		int maxValue = 100;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			mergeSort1(arr1);
			mergeSort2(arr2);
			if (!isEqual(arr1, arr2)) {
				System.out.println("出错了！");
				printArray(arr1);
				printArray(arr2);
				break;
			}
		}
		System.out.println("测试结束");
	}
//	public static void main(String[] args) {
//		int[] arr = new int[]{3,2};
//		mergeSort2(arr);
//		printArray(arr);
//	}
}
